package demo;

import ru.nsu.fit.iconveyor.conveyor.node.InnerNode;
import ru.nsu.fit.iconveyor.conveyor.node.NodeConnection;

import java.util.*;

/**
 * @author Timur Zolotuhin (tzolotuhin@gmail.com)
 */
public class SplitNode extends InnerNode {
    public static final String INPUT_ARG = "input_arg";
    public static final String INPUT_ARG_LOOP = "input_arg_loop";
    public static final String OUTPUT_ARG_LOOP = "output_arg_loop";
    public static final String OUTPUT_RESULT = "output_result";

    public static final String SPLIT_ROW_COUNT_PARAMETER = "SPLIT_ROW_COUNT_PARAMETER";
    public static final String SPLIT_COLUMN_COUNT_PARAMETER = "SPLIT_COLUMN_COUNT_PARAMETER";

    private final NodeConnection<Image> inputArg;
    private final NodeConnection<ArrayDeque> inputArgLoop;
    private final NodeConnection<ArrayDeque> outputArgLoop;
    private final NodeConnection<Image> outputResult;

    public SplitNode(String id) {
        super(id);

        inputArg = new NodeConnection<Image>(Image.class, NodeConnection.Type.INPUT, INPUT_ARG);
        inputArgLoop = new NodeConnection<ArrayDeque>(ArrayDeque.class, NodeConnection.Type.INPUT, INPUT_ARG_LOOP);
        outputArgLoop = new NodeConnection<ArrayDeque>(ArrayDeque.class, NodeConnection.Type.OUTPUT, OUTPUT_ARG_LOOP);
        outputResult = new NodeConnection<Image>(Image.class, NodeConnection.Type.OUTPUT, OUTPUT_RESULT);

        addNodeConnection(inputArg);
        addNodeConnection(inputArgLoop);
        addNodeConnection(outputArgLoop);
        addNodeConnection(outputResult);
    }

    @Override
    public boolean isParallelExecute() {
        return false;
    }

    @Override
    public Map<String, Object> execute(Map<String, Object> inputBundles, Properties prop) {
        Map<String, Object> result = new HashMap<String, Object>();

        ArrayDeque<Image> chunks = new ArrayDeque<Image>();
        if (inputBundles.get(INPUT_ARG_LOOP) != null) {
            Object inputArgLoopBundle = inputBundles.get(INPUT_ARG_LOOP);
            if (inputArgLoopBundle != null && inputArgLoopBundle.getClass() == ArrayDeque.class) {
                chunks = (ArrayDeque<Image>)inputArgLoopBundle;
            }
        }

        if (inputBundles.get(INPUT_ARG) != null) {
            Object inputArgBundle = inputBundles.get(INPUT_ARG);
            if (inputArgBundle != null && inputArgBundle.getClass() != null && inputArgBundle.getClass() == Image.class) {
                int splitRowCount = 1;
                int splitColumnCount = 1;

                Object rowObj = getParameter(SPLIT_ROW_COUNT_PARAMETER);
                Object columnObj = getParameter(SPLIT_COLUMN_COUNT_PARAMETER);

                try {
                    if (rowObj != null)
                        splitRowCount = new Integer(rowObj.toString());
                    if (columnObj != null)
                        splitColumnCount = new Integer(columnObj.toString());
                } catch (Exception ex) {}

                if (splitColumnCount < 1)
                    splitColumnCount = 1;
                if (splitRowCount < 1)
                    splitRowCount = 1;

                Image inputImage = (Image)inputArgBundle;
                int[][] array = inputImage.getBitmap();

                int outputSizeX = 0;
                int outputSizeY = 0;
                if (array.length % splitColumnCount == 0)
                    outputSizeX = array.length / splitColumnCount;
                else
                    outputSizeX = (array.length / splitColumnCount) + 1;

                if (array[0].length % splitRowCount == 0)
                    outputSizeY = array[0].length / splitRowCount;
                else
                    outputSizeY = (array[0].length / splitRowCount) + 1;

                for (int i = 0; i < splitColumnCount; i++) {
                    for (int j = 0; j < splitRowCount; j++) {
                        int[][] chunkBitmap = null;
                        if (i == splitColumnCount - 1 && j == splitRowCount - 1) {
                            chunkBitmap = new int[array.length - i * outputSizeX][array[0].length - j * outputSizeY];
                        } else if (i == splitColumnCount - 1) {
                            chunkBitmap = new int[array.length - i * outputSizeX][outputSizeY];
                        } else if (j == splitRowCount - 1) {
                            chunkBitmap = new int[outputSizeX][array[0].length - j * outputSizeY];
                        } else {
                            chunkBitmap = new int[outputSizeX][outputSizeY];
                        }
                        for (int x = 0; x < outputSizeX; x++) {
                            for (int y = 0; y < outputSizeY; y++) {
                                if (x + i * outputSizeX < array.length && y + j * outputSizeY < array[0].length)
                                    chunkBitmap[x][y] = array[x + i * outputSizeX][y + j * outputSizeY];
                            }
                        }
                        Image chunkImage = new Image();
                        chunkImage.setBitmap(chunkBitmap);
                        chunkImage.setName(inputImage.getName());
                        chunks.add(chunkImage);
                    }
                }
            }
        }

        if (chunks.size() > 0) {
            result.put(OUTPUT_RESULT, chunks.poll());
        }
        if (chunks.size() > 0) {
            result.put(OUTPUT_ARG_LOOP, chunks);
        }

        return result;
    }

    @Override
    public Map<String, Object> preExecute(Map<String, Object> availableInputBundles, Properties prop) {
        if (availableInputBundles.get(INPUT_ARG) != null || availableInputBundles.get(INPUT_ARG_LOOP) != null) {
            return availableInputBundles;
        }
        return null;
    }

    @Override
    public int getOutputToInputRatio() {
        //TODO: check
        return 2;
    }
}
